//
// Created by vivi on 17/02/2018.
//

#pragma once

#include "core/engine/hairengine.h"
#include "core/hairgeo/hairgeo.h"
#include <Eigen/Eigen>
#include <string>
#include <memory>

namespace HairEngine {

	/*
	 * StrandBasedHairEngine is a special type of HairEngine, it treats the whole hair geometry as a collective
	 * strands, a strand is composed of several discrete particles.
	 */
	class StrandBasedHairEngine: public HairEngine {
	Public:

		/*
        * A simple ParticleIterator adaptor that used to create a iterator for StrandBasedHairGeometry's "assignParticlePositions"
        * and initialization
        */
		class ParticleIterator {
		Public:

			ParticleIterator(const StrandBasedHairEngine *hairEnginePtr_, size_t strandIndex_, size_t particleLocalIndex_);

			inline bool operator==(const ParticleIterator & other) const;
			inline bool operator!=(const ParticleIterator & other) const;
			inline ParticleIterator operator++(int); //Postfix
			inline ParticleIterator & operator++(); //Prefix

			inline bool isValidIterator() const; //Check whether this iterator is valid for the hairEnginePtr

			inline const Eigen::Vector3f &operator*() const;

		Private:
			const StrandBasedHairEngine *hairEnginePtr;
			size_t strandIndex;
			size_t particleLocalIndex;
		};

		/*
		 * A simple StrandCountIterator adaptor that used to create a iterator for StrandBasedHairGeometry's initialization
		 */
		class StrandCountIterator {
		Public:

			StrandCountIterator(const StrandBasedHairEngine *hairEnginePtr_, size_t strandIndex_);

			inline bool operator==(const StrandCountIterator & other) const;
			inline bool operator!=(const StrandCountIterator & other) const;
			inline StrandCountIterator operator++(int); //Postfix
			inline StrandCountIterator & operator++(); //Prefix

			inline size_t operator*() const;

			inline bool isValidIterator() const; //Check whether this iterator is valid for the hairEnginePtr

		Private:
			const StrandBasedHairEngine *hairEnginePtr;
			size_t strandIndex;
		};

	Public:
		typedef StrandBasedHairEngine *Ptr;

		using HairGeometry = StrandBasedHairGeometry;
		friend class ParticleIterator;
	Public:

		//Delegate for implementation
		inline virtual size_t getParticleCount() const = 0;
		inline virtual size_t getStrandCount() const = 0;
		inline virtual size_t getParticleCountOnStrand(size_t strandNumber) const = 0;
		inline virtual const Eigen::Vector3f & getParticlePosition(size_t strandNumber, size_t localNumber) const = 0;

		inline ParticleIterator getBeginParticleIterator() const;
		inline ParticleIterator getEndParticleIterator() const;
		inline StrandCountIterator getBeginStrandCountIterator() const;
		inline StrandCountIterator getEndStrandCountIterator() const;

	Protected:

		/*
		 * In the StrandBasedHairEngine, we might provide a method for writing the simulated hair geometry to a file
		 * (.hair). We owns a StrandBasedHairGeometry pointer and use the "assignParticlePositions" and "writeToFile"
		 * method to write the simulated hair geometry to a file.
		 */

		mutable std::unique_ptr<StrandBasedHairGeometry> hairGeometryPtr = nullptr;

	Public:
		void writeToFile(const std::string & filePath) const;
		void writeToDirectory(const std::string & directoryPath, const std::string & fileName) const;
	};
}
